//Caelum
#include "SkyGenerator.h"
#include "GameSystemManager.h"
#include "GameSceneManager.h"
#include "XMLHandler.h"
#include "CVelocity.h"
#include "CInputPlayer.h"

SkyConfig sky_config;

SkyGenerator::SkyGenerator()
{
	mSceneMgr = GameSceneManager::getSingleton()->getSceneManager();
	mRoot = GameSceneManager::getSingleton()->getRoot();
	mWindow = GameSystemManager::getSingleton()->getOgre()->getWindow();
	sky_config.isLoaded = false;
	setDefaultConfig();
	//mCaelumSystem = NULL;
}

SkyGenerator::~SkyGenerator()
{
	
}

void SkyGenerator::UpdateSun()
{
    // Update the haze sun light position
    if (mCaelumSystem->getSun ()) {
	    Ogre::MaterialPtr mat = static_cast<Ogre::MaterialPtr>(Ogre::MaterialManager::getSingleton ().getByName ("CaelumDemoTerrain"));
        assert(mat->getTechnique (0)->getPass ("CaelumHaze"));
	    mat->getTechnique (0)->getPass ("CaelumHaze")->getVertexProgramParameters ()->setNamedConstant ("sunDirection", mCaelumSystem->getSun ()->getSunDirection ());
    }
}

void SkyGenerator::setDefaultConfig() {
	sky_config.isLoaded = false;
	sky_config.ambientLight = Ogre::ColourValue(0.5, 0.5, 0.5);
	sky_config.diffuseLight = Ogre::ColourValue(3.0, 3.0, 2.7);
	sky_config.specularLight = Ogre::ColourValue(5.0, 5.0, 5.0);
	sky_config.manageLights = true;
	sky_config.cloudsSpeed = 0.000005;
	sky_config.cloudsCover = 0.5;
	sky_config.cloudsBendTime = 24;
	sky_config.timeScale = 400;

	sky_config.backgroundColour = Ogre::ColourValue(0.5, 0.5, 0.5);
	sky_config.NearClip = 1.0f;
	sky_config.AspectRatio = 1.33333f;
	sky_config.FarClip = 2000.0;

	sky_config.FogType = Ogre::FOG_NONE;
	sky_config.FogLineStart = 0.0f;
	sky_config.FogLineEnd = 0.0f;
	sky_config.FogDensity = 0.0f;
	sky_config.FogColour = Ogre::ColourValue(0.5, 0.5, 0.5);

	//sky_config.ProjType = Ogre::ProjectionType::PT_ORTHOGRAPHIC;
	sky_config.ProjType = Ogre::ProjectionType::PT_PERSPECTIVE;
	sky_config.FOVy = Radian(Degree(45));

	sky_config.skyMode = "Skybox";
	sky_config.skyBoxName = "";
	sky_config.skyBoxDistance = 1000;
}

void SkyGenerator::createSky() {
	//Setup Ogre viewport
	setupScene();

	//Setup Sky system
	if(sky_config.isLoaded) {
		if(sky_config.skyMode == "Caelum")
			setupCaelum();
		else
			setupSkyBox();

		sky_config.isLoaded = true;
	} 
}

void SkyGenerator::setupSkyBox() {
	if(sky_config.skyBoxName != "")
		GameSceneManager::getSingleton()->getSceneManager()->setSkyBox(true, sky_config.skyBoxName, sky_config.skyBoxDistance);
}
void SkyGenerator::destroySkyBox() {
	if(sky_config.skyBoxName != "")
		GameSceneManager::getSingleton()->getSceneManager()->setSkyBox(false, sky_config.skyBoxName, sky_config.skyBoxDistance);
}

void SkyGenerator::setupScene() {
	/*
	sky_config.FogType = 0;
	sky_config.FogLineStart = 0.0f;
	sky_config.FogLineEnd = 0.0f;
	sky_config.FogDensity = 0.0f;
	sky_config.FogColour = Ogre::ColourValue(0.5, 0.5, 0.5);
	*/
	GameSceneManager::getSingleton()->getSceneManager()->setAmbientLight(sky_config.ambientLight);
	GameSceneManager::getSingleton()->getViewport()->setBackgroundColour(sky_config.backgroundColour);
	GameSceneManager::getSingleton()->getCamera()->setNearClipDistance(sky_config.NearClip);
	GameSceneManager::getSingleton()->getCamera()->setFarClipDistance(sky_config.FarClip);
	GameSceneManager::getSingleton()->getCamera()->setAspectRatio(sky_config.AspectRatio);

	//GameSceneManager::getSingleton()->getCamera()->setAspectRatio(Real(vp->getActualWidth()) / Real(vp->getActualHeight())); 
	GameSceneManager::getSingleton()->getCamera()->setFOVy(sky_config.FOVy);
	GameSceneManager::getSingleton()->getCamera()->setProjectionType(sky_config.ProjType);
	
	CVelocity *obj_vel = dynamic_cast<CVelocity*>(GameObjectManager::getSingleton()->getGO("Camera")->getComponent("CVelocity"));
	if(obj_vel)
		obj_vel->Velocity = sky_config.camVel;

	GameSceneManager::getSingleton()->getCamera()->setPosition(sky_config.camPos);

	//sky_config.ProjectionType = Ogre::ProjectionType::PT_ORTHOGRAPHIC;
	//sky_config.FOVy = Radian(Degree(45));

	//setFOVy()

	Ogre::FogMode fog_type;

	if(sky_config.FogType == 0)
		fog_type = Ogre::FOG_NONE;
		
	if(sky_config.FogType == 1)
		fog_type = Ogre::FOG_EXP;

	if(sky_config.FogType == 2)
		fog_type = Ogre::FOG_EXP2;

	if(sky_config.FogType == 3)
		fog_type = Ogre::FOG_LINEAR;

	GameSceneManager::getSingleton()->getSceneManager()->setFog(fog_type,sky_config.FogColour,sky_config.FogDensity,sky_config.FogLineStart,sky_config.FogLineEnd);

}

void SkyGenerator::destroySky() {
	if(sky_config.isLoaded) {
		if(sky_config.skyMode == "Caelum")
			mCaelumSystem->shutdown();
		else
			destroySkyBox();
	}
}

void SkyGenerator::saveAttrib(TiXmlElement* mElem)
{
	mElem->SetAttribute("isLoaded",Ogre::StringConverter::toString(sky_config.isLoaded).c_str());
	mElem->SetAttribute("ambientLightRed",Ogre::StringConverter::toString(sky_config.ambientLight.r).c_str());
	mElem->SetAttribute("ambientLightGreen",Ogre::StringConverter::toString(sky_config.ambientLight.g).c_str());
	mElem->SetAttribute("ambientLightBlue",Ogre::StringConverter::toString(sky_config.ambientLight.b).c_str());

	mElem->SetAttribute("diffuseLightRed",Ogre::StringConverter::toString(sky_config.diffuseLight.r).c_str());
	mElem->SetAttribute("diffuseLightGreen",Ogre::StringConverter::toString(sky_config.diffuseLight.g).c_str());
	mElem->SetAttribute("diffuseLightBlue",Ogre::StringConverter::toString(sky_config.diffuseLight.b).c_str());
	
	mElem->SetAttribute("specularLightRed",Ogre::StringConverter::toString(sky_config.specularLight.r).c_str());
	mElem->SetAttribute("specularLightGreen",Ogre::StringConverter::toString(sky_config.specularLight.g).c_str());
	mElem->SetAttribute("specularLightBlue",Ogre::StringConverter::toString(sky_config.specularLight.b).c_str());

	mElem->SetAttribute("manageLights",Ogre::StringConverter::toString(sky_config.manageLights).c_str());
	mElem->SetAttribute("cloudsSpeed",Ogre::StringConverter::toString(sky_config.cloudsSpeed).c_str());
	mElem->SetAttribute("cloudsCover",Ogre::StringConverter::toString(sky_config.cloudsCover).c_str());
	mElem->SetAttribute("cloudsBendTime",Ogre::StringConverter::toString(sky_config.cloudsBendTime).c_str());
	mElem->SetAttribute("timeScale",Ogre::StringConverter::toString(sky_config.timeScale).c_str());

	mElem->SetAttribute("timeScale",Ogre::StringConverter::toString(sky_config.timeScale).c_str());
	
	mElem->SetAttribute("backgroundColour",Ogre::StringConverter::toString(sky_config.backgroundColour).c_str());
	mElem->SetAttribute("NearClip",Ogre::StringConverter::toString(sky_config.NearClip).c_str());
	mElem->SetAttribute("AspectRatio",Ogre::StringConverter::toString(sky_config.AspectRatio).c_str());
	mElem->SetAttribute("FarClip",Ogre::StringConverter::toString(sky_config.FarClip).c_str());
	
	if(sky_config.FogType == FOG_NONE)
		mElem->SetAttribute("FogType","FOG_NONE");
	if(sky_config.FogType == FOG_EXP)
		mElem->SetAttribute("FogType","FOG_EXP");
	if(sky_config.FogType == FOG_EXP2)
		mElem->SetAttribute("FogType","FOG_EXP2");
	if(sky_config.FogType == FOG_LINEAR)
		mElem->SetAttribute("FogType","FOG_LINEAR");

	mElem->SetAttribute("FogLineStart",Ogre::StringConverter::toString(sky_config.FogLineStart).c_str());
	mElem->SetAttribute("FogLineEnd",Ogre::StringConverter::toString(sky_config.FogLineEnd).c_str());
	mElem->SetAttribute("FogDensity",Ogre::StringConverter::toString(sky_config.FogDensity).c_str());
	mElem->SetAttribute("FogColour",Ogre::StringConverter::toString(sky_config.FogColour).c_str());
	
	if(sky_config.ProjType == Ogre::ProjectionType::PT_PERSPECTIVE)
		mElem->SetAttribute("ProjectionType","PERSPECTIVE");
	
	if(sky_config.ProjType == Ogre::ProjectionType::PT_ORTHOGRAPHIC)
		mElem->SetAttribute("ProjectionType","ORTHOGRAPHIC");

	mElem->SetAttribute("FOVy",Ogre::StringConverter::toString(sky_config.FOVy).c_str());
	
	mElem->SetAttribute("SkyMode",sky_config.skyMode.c_str());
	mElem->SetAttribute("SkyBoxName",sky_config.skyBoxName.c_str());
	mElem->SetAttribute("SkyDistance",Ogre::StringConverter::toString(sky_config.skyBoxDistance).c_str());

	CInputPlayer *ci = dynamic_cast<CInputPlayer*>(GameObjectManager::getSingleton()->getGO("Camera")->getComponent("CInputPlayer"));
	if(ci) {
		mElem->SetAttribute("camPosition",Ogre::StringConverter::toString(ci->cameraNode->getPosition()).c_str());
		Ogre::Quaternion q = ci->cameraYawNode->getOrientation() *	ci->cameraPitchNode->getOrientation();
		mElem->SetAttribute("camYaw",Ogre::StringConverter::toString(ci->cameraYawNode->getOrientation()).c_str());
		mElem->SetAttribute("camPitch",Ogre::StringConverter::toString(ci->cameraPitchNode->getOrientation()).c_str());
	}
	mElem->SetAttribute("camVelocity",Ogre::StringConverter::toString(sky_config.camVel).c_str());

	/*
	sky_config.backgroundColour = Ogre::ColourValue(0.5, 0.5, 0.5);
	sky_config.NearClip = 0.0f;
	sky_config.AspectRatio = 0.0f;
	sky_config.FarClip = 0.0f;

	sky_config.FogType = 0;
	sky_config.FogLineStart = 0.0f;
	sky_config.FogLineEnd = 0.0f;
	sky_config.FogDensity = 0.0f;
	sky_config.FogColour = Ogre::ColourValue(0.5, 0.5, 0.5);
	*/
}

void SkyGenerator::setAttribute(std::string name,std::string  value)
{
	if(name == "isLoaded")
		sky_config.isLoaded = Ogre::StringConverter::parseBool(value);
	if(name == "ambientLightRed")
		sky_config.ambientLight.r = Ogre::StringConverter::parseReal(value);
	if(name == "ambientLightGreen")
		sky_config.ambientLight.g = Ogre::StringConverter::parseReal(value);
	if(name == "ambientLightBlue")
		sky_config.ambientLight.b = Ogre::StringConverter::parseReal(value);
	if(name == "diffuseLightRed")
		sky_config.diffuseLight.r = Ogre::StringConverter::parseReal(value);;
	if(name == "diffuseLightGreen")
		sky_config.diffuseLight.g = Ogre::StringConverter::parseReal(value);
	if(name == "diffuseLightBlue")
		sky_config.diffuseLight.b = Ogre::StringConverter::parseReal(value);
	if(name == "specularLightRed")
		sky_config.specularLight.r = Ogre::StringConverter::parseReal(value);
	if(name == "specularLightGreen")
		sky_config.specularLight.g = Ogre::StringConverter::parseReal(value);
	if(name == "specularLightBlue")
		sky_config.specularLight.b = Ogre::StringConverter::parseReal(value);
	if(name == "manageLights")
		sky_config.manageLights = Ogre::StringConverter::parseBool(value);
	if(name == "cloudsSpeed")
		sky_config.cloudsSpeed = Ogre::StringConverter::parseReal(value);
	if(name == "cloudsCover")
		sky_config.cloudsCover = Ogre::StringConverter::parseReal(value);
	if(name == "cloudsBendTime")
		sky_config.cloudsBendTime = Ogre::StringConverter::parseReal(value);
	if(name == "timeScale")
		sky_config.timeScale = Ogre::StringConverter::parseReal(value);

	if(name == "backgroundColour")
		sky_config.backgroundColour = Ogre::StringConverter::parseColourValue(value);
	if(name == "NearClip")
		sky_config.NearClip = Ogre::StringConverter::parseReal(value);
	if(name == "AspectRatio")
		sky_config.AspectRatio = Ogre::StringConverter::parseReal(value);
	if(name == "FarClip")
		sky_config.FarClip = Ogre::StringConverter::parseReal(value);
	if(name == "FogType") {
		if(value == "FOG_NONE")
			sky_config.FogType = Ogre::FogMode::FOG_NONE;
		if(value == "FOG_EXP")
			sky_config.FogType = Ogre::FogMode::FOG_EXP;
		if(value == "FOG_EXP2")
			sky_config.FogType = Ogre::FogMode::FOG_EXP2;
		if(value == "FOG_LINEAR")
			sky_config.FogType = Ogre::FogMode::FOG_LINEAR;
	}
	if(name == "FogLineStart")
		sky_config.FogLineStart = Ogre::StringConverter::parseReal(value);
	if(name == "FogLineEnd")
		sky_config.FogLineEnd = Ogre::StringConverter::parseReal(value);
	if(name == "FogDensity")
		sky_config.FogDensity = Ogre::StringConverter::parseReal(value);
	if(name == "FogColour")
		sky_config.FogColour = Ogre::StringConverter::parseColourValue(value);

	if(name == "ProjectionType") {
		if(value == "PERSPECTIVE")
			sky_config.ProjType = Ogre::ProjectionType::PT_PERSPECTIVE;

		if(value == "ORTHOGRAPHIC")
			sky_config.ProjType = Ogre::ProjectionType::PT_ORTHOGRAPHIC;
	}

	if(name == "FOVy")
		sky_config.FOVy = Radian(Degree(Ogre::StringConverter::parseReal(value)));

	if(name == "SkyMode")
		sky_config.skyMode = value;
	if(name == "SkyBoxName")
		sky_config.skyBoxName = value;
	if(name == "SkyDistance")
		sky_config.skyBoxDistance = Ogre::StringConverter::parseReal(value);
	if(name == "camPosition") {
		CInputPlayer *ci = dynamic_cast<CInputPlayer*>(GameObjectManager::getSingleton()->getGO("Camera")->getComponent("CInputPlayer"));
		if(ci)
			ci->cameraNode->setPosition(Ogre::StringConverter::parseVector3(value));
	}
	if(name == "camYaw") {
		//GameSystemManager::getSingleton()->getInput()->Yaw = Ogre::StringConverter::parseReal(value);
		CInputPlayer *ci = dynamic_cast<CInputPlayer*>(GameObjectManager::getSingleton()->getGO("Camera")->getComponent("CInputPlayer"));
		if(ci) {
			ci->cameraYawNode->setOrientation(Ogre::StringConverter::parseQuaternion(value));
		}
	}
	if(name == "camPitch") {
		//GameSystemManager::getSingleton()->getInput()->Pitch = Ogre::StringConverter::parseReal(value);
		CInputPlayer *ci = dynamic_cast<CInputPlayer*>(GameObjectManager::getSingleton()->getGO("Camera")->getComponent("CInputPlayer"));
		if(ci) {
			ci->cameraPitchNode->setOrientation(Ogre::StringConverter::parseQuaternion(value));
			//ci->cameraYawNode->yaw(GameSystemManager::getSingleton()->getInput()->Yaw);
			//ci->cameraPitchNode->pitch(GameSystemManager::getSingleton()->getInput()->Pitch);

			//ci->cameraNode->translate(ci->cameraYawNode->getOrientation() *	ci->cameraPitchNode->getOrientation() * Ogre::Vector3(1,1,1),Ogre::SceneNode::TS_LOCAL);
		}
	}
	if(name == "camVelocity") {
		CEGUI::Spinner* spn = static_cast<CEGUI::Spinner*>(GameSystemManager::getSingleton()->getGUI()->getElement("MenuBar/camVelocity"));
		spn->setText(value);
	}

	/*
	sky_config.backgroundColour = Ogre::ColourValue(0.5, 0.5, 0.5);
	sky_config.NearClip = 0.0f;
	sky_config.AspectRatio = 0.0f;
	sky_config.FarClip = 0.0f;

	sky_config.FogType = 0;
	sky_config.FogLineStart = 0.0f;
	sky_config.FogLineEnd = 0.0f;
	sky_config.FogDensity = 0.0f;
	sky_config.FogColour = Ogre::ColourValue(0.5, 0.5, 0.5);
	*/
}

//Under construction
void SkyGenerator::setupCaelum(void)
{	 

	// Pick components to create in the demo.
    // You can comment any of those and it should still work
    // It makes little sense to comment the first three.
    caelum::CaelumSystem::CaelumComponent componentMask = 
            static_cast<caelum::CaelumSystem::CaelumComponent> (
            caelum::CaelumSystem::CAELUM_COMPONENT_SKY_COLOUR_MODEL |
            caelum::CaelumSystem::CAELUM_COMPONENT_SUN |
            caelum::CaelumSystem::CAELUM_COMPONENT_SOLAR_SYSTEM_MODEL |
            caelum::CaelumSystem::CAELUM_COMPONENT_SKY_DOME |
            caelum::CaelumSystem::CAELUM_COMPONENT_STARFIELD |
            caelum::CaelumSystem::CAELUM_COMPONENT_CLOUDS |
            //caelum::CaelumSystem::CAELUM_COMPONENT_GROUND_FOG |
            0);

	// Initialise Caelum
	mCaelumSystem = new caelum::CaelumSystem (mRoot, mSceneMgr, componentMask); //mRoot

    // KNOWN BUG: The horizon is pure white if setManageFog is false.
    // I blame it on the transparent skydome.
    mCaelumSystem->setManageSceneFog(true);
    mCaelumSystem->setSceneFogDensityMultiplier(0.00015);

	// Setup sun options
    if (mCaelumSystem->getSun ()) {
	    mCaelumSystem->getSun ()->setAmbientMultiplier (sky_config.ambientLight);
	    mCaelumSystem->getSun ()->setDiffuseMultiplier (sky_config.diffuseLight);
	    // For green terrain:
	    //mCaelumSystem->getSun ()->setDiffuseMultiplier (Ogre::ColourValue(0.1, 3, 0.1));
	    mCaelumSystem->getSun ()->setSpecularMultiplier (sky_config.specularLight);
	    mCaelumSystem->getSun ()->setManageAmbientLight (sky_config.manageLights);
    }

    // Setup fog options.
    if (mCaelumSystem->getGroundFog()) {
        mCaelumSystem->getGroundFog()->findFogPassesByName();
    }

    // Setup cloud options.
    // Tweak these settings to make the demo look pretty.
    if (mCaelumSystem->getClouds ()) {
        mCaelumSystem->getClouds ()->setCloudSpeed(Ogre::Vector2(sky_config.cloudsSpeed, -0.000009));
        mCaelumSystem->getClouds ()->setCloudBlendTime(3600 * sky_config.cloudsBendTime);	
        mCaelumSystem->getClouds ()->setCloudCover(sky_config.cloudsCover);
    }

	// Setup starfield options
    if (mCaelumSystem->getStarfield ()) {
	    mCaelumSystem->getStarfield ()->setInclination (Degree (13));
    }

	// Set time acceleration.
	mCaelumSystem->getUniversalClock ()->setTimeScale(sky_config.timeScale);

    // Winter dawn in the southern hemisphere, looking north
	mCaelumSystem->getUniversalClock ()->setGregorianDateTime (2008, 7, 4, 20, 33, 0);
    mCaelumSystem->getSolarSystemModel ()->setObserverLongitude (Ogre::Degree(151 + 12.0 / 60));

    // Sidney
    mCaelumSystem->getSolarSystemModel ()->setObserverLatitude (Ogre::Degree(-33 + 52.0 / 60));
    // Beyond the southern polar circle, no sunrise
    //mCaelumSystem->getSolarSystemModel ()->setObserverLatitude (Ogre::Degree(-70));
    // Beyond the northern polar circle, no sunset
    //mCaelumSystem->getSolarSystemModel ()->setObserverLatitude (Ogre::Degree(70));

	// Register caelum to the render target
	mWindow->addListener (mCaelumSystem);

	//std::string terrain_cfg("CaelumDemoTerrain.cfg");
	//std::string terrain_cfg("Terrain2.cfg");
	//mSceneMgr->setWorldGeometry (terrain_cfg);
	
	
}

